﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Xml.Linq;
#if NETSTANDARD2_0
using Microsoft.AspNetCore.Http;
#endif
#if NET40 || NET45
using System.Web;
#endif
using WeShare.WeiXin.Common;
#if !NET40
using System.Threading.Tasks;
#endif
namespace WeShare.WeiXin.Pay
{
    public static class PayApi
    {
        /// <summary>
        /// 获取扫码支付url
        /// </summary>
        /// <param name="req"></param>
        /// <param name="isConvertShortUrl">是否转换成短url</param>
        /// <param name="mchId"></param>
        /// <returns></returns>
        public static string ScanPayUrl(ScanPayReq req, bool isConvertShortUrl = false, string mchId = "")
        {
            var payconfig = OpenApi.GetPayConfig(mchId);
            var param = PayTools.CombineParam(req, payconfig);
            var arr = param.Where(sw => !string.IsNullOrEmpty(sw.Value)).
                Select(d => $"{d.Key}={d.Value}").ToArray();
            string stringA = string.Join("&", arr);
            var url = $"weixin://wxpay/bizpayurl?{stringA}";
            if (isConvertShortUrl)
            {
                var result = ToShortUrl(new ToShortUrlReq { long_url = url, sub_mch_id = req.sub_mch_id, sub_appid = req.sub_appid });
                url = result.short_url;
            }
            return url;
        }
        public static ToShortUrlRes ToShortUrl(ToShortUrlReq req, string mchId = "")
        {
            var url = "https://api.mch.weixin.qq.com/tools/shorturl";
            return req.PayRequest<ToShortUrlRes>(url, mchId);
        }
        /// <summary>
        /// 刷卡支付
        /// </summary>
        /// <param name="req"></param>
        /// <param name="mchId"></param>
        /// <returns></returns>
        public static MicroPayRes MicroPay(MicroPayReq req, string mchId = "")
        {
            var url = "https://api.mch.weixin.qq.com/pay/micropay";
            return req.PayRequest<MicroPayRes>(url, mchId);
        }
        /// <summary>
        /// 退款接口
        /// </summary>
        /// <param name="req"></param>
        /// <param name="mchId"></param>
        /// <returns></returns>
        public static RefundRes Refund(RefundReq req, string mchId = "")
        {
            var url = $"https://api.mch.weixin.qq.com/secapi/pay/refund";
            return req.PayRequest<RefundRes>(url, mchId);
        }
        /// <summary>
        /// 下载对账单。如果启用压缩，请将文件流保存为gzip格式。否则保存为csv
        /// </summary>
        /// <param name="req"></param>
        /// <param name="stream"></param>
        /// <param name="mchId"></param>
        /// <returns></returns>
        public static BasePayRes DownloadBill(DownloadBill req, FileStreamInfo stream, string mchId = "")
        {
            var url = $"https://api.mch.weixin.qq.com/pay/downloadbill";
            var res = req.DownLoad(url, stream, mchId);
            if (res.Contains("return_code"))
            {
                return PayTools.XmlToEntity<BasePayRes>(res, "");
            }

            if (!string.IsNullOrEmpty(res))
            {
                var bytes = Encoding.UTF8.GetBytes(res);
                using (var ms = new MemoryStream(bytes))
                {
                    ms.CopyTo(stream);
                    stream.Flush();
                }
            }
            return new BasePayRes { return_code = "SUCCESS", err_code = "SUCCESS" };
        }
        /// <summary>
        /// 获取退款订单
        /// </summary>
        /// <param name="req"></param>
        /// <param name="mchId"></param>
        /// <returns></returns>
        public static QueryRefundOrderRes QueryRefundOrder(QueryRefundOrderReq req, string mchId = "")
        {
            var url = "https://api.mch.weixin.qq.com/pay/refundquery";
            return req.PayRequest<QueryRefundOrderRes>(url, mchId);
        }
        /// <summary>
        /// 关闭订单。以下情况需要调用关单接口：商户订单支付失败需要生成新单号重新发起支付，要对原订单号调用关单，避免重复支付；系统下单后，用户支付超时，系统退出不再受理，避免用户继续，请调用关单接口。
        /// 注意：订单生成后不能马上调用关单接口，最短调用时间间隔为5分钟。
        /// </summary>
        /// <param name="req"></param>
        /// <param name="mchId"></param>
        /// <returns></returns>
        public static BasePayRes CloseOrder(CloseOrderReq req, string mchId = "")
        {
            var url = $"https://api.mch.weixin.qq.com/pay/closeorder";
            return req.PayRequest<BasePayRes>(url, mchId);
        }
        /// <summary>
        ///刷卡支付， 并处理验证密码的过程。推荐使用此方法
        /// </summary>
        /// <param name="req"></param>
        /// <returns></returns>
        public static MicroPayValidPwdRes MicroPayByVaildPwd(MicroPayReq req, string mchId = "")
        {
            var result = new MicroPayValidPwdRes();
            var res = MicroPay(req, mchId);
            if (res.result_code == res.return_code && res.result_code == "SUCCESS")
            {
                result.IsSuccess = true;
                result.OpenId = res.openid;
                result.TradeNo = res.transaction_id;
                return result;
            }

            if (res.err_code == "USERPAYING")
            {
                for (int i = 0; i < 10; i++)
                {
                    Thread.Sleep(3000);
                    var orderinfo = QueryOrderInfo(new QueryOrderReq
                    {
                        sub_mch_id = req.sub_mch_id,
                        out_trade_no = req.out_trade_no
                    }, mchId);
                    if (orderinfo.trade_state == TradeState.USERPAYING)
                    {
                        continue;
                    }
                    if (orderinfo.trade_state == TradeState.SUCCESS)
                    {

                        result.IsSuccess = true;
                        result.OpenId = orderinfo.openid;
                        result.TradeNo = orderinfo.transaction_id;
                        return result;
                    }
                }
                var re = Reverse(new ReverseReq
                {
                    sub_mch_id = req.sub_mch_id,
                    out_trade_no = req.out_trade_no
                }, mchId);
                result.IsSuccess = false;
                result.ErrMsg = "验证密码超时，支付失败";
                return result;
            }
            else
            {
                result.IsSuccess = false;
                result.ErrMsg = res.err_code;
                return result;
            }
        }
        /// <summary>
        /// 统一下单接口
        /// </summary>
        /// <param name="req"></param>
        /// <returns></returns>
        public static UnifiedRes UnifiedOrder(UnifiedReq req, string mchId = "")
        {
            var url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
            return req.PayRequest<UnifiedRes>(url, mchId);
        }
        public static MicroPayRes MicroPay(MicroPayReq req, Action<string> userPayingAction, string mchId = "")
        {
            var url = "https://api.mch.weixin.qq.com/pay/micropay";
            var res = req.PayRequest<MicroPayRes>(url, mchId);
            if (res.result_code == "USERPAYING")
            {
                userPayingAction(req.out_trade_no);
            }
            return res;
        }
        /// <summary>
        /// 查询订单状态
        /// </summary>
        /// <param name="req"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static QueryOrderRes QueryOrderInfo(QueryOrderReq req, string mchId = "")
        {
            var url = "https://api.mch.weixin.qq.com/pay/orderquery";
            return req.PayRequest<QueryOrderRes>(url, mchId);
        }
        public static ReverseRes Reverse(ReverseReq req, string mchId = "")
        {
            var url = "https://api.mch.weixin.qq.com/secapi/pay/reverse";
            var res = req.PayRequest<ReverseRes>(url, mchId);
            if (res.recall == "Y")
            {
                Thread.Sleep(2000);
                return Reverse(req);
            }
            return res;
        }

        public static string GetScanPayNotify(this HttpContext context, Func<ScanPayNotifyReq, string> func, string mchId = "")
        {
            var reqdata = context.GetRequestData();
            var payconfig = OpenApi.GetPayConfig(mchId);
            var doc = XElement.Parse(reqdata);
            var rev = XmlUtil.ToObject<ScanPayNotifyReq>(doc);
            var prepayId = func.Invoke(rev);
            var res = new ScanPayRes
            {
                prepay_id = prepayId
            };
            var param = PayTools.CombineParam(res, payconfig);
            var xml = Utils.parseXML(param);//将数据集合转换成XML
            return xml;
        }
        public static string GetNotifyRes(this HttpContext context, Func<OrderInfo, bool> action, Action<string, Exception> errCallBack = null, string mchId = "")
        {
            var reqdata = context.GetRequestData();

            try
            {
                var payconfig = OpenApi.GetPayConfig(mchId);
                var doc = XElement.Parse(reqdata);
                var rev = XmlUtil.ToObject<OrderInfo>(doc);
                if (rev.return_code != "SUCCESS")
                { return BackMessage("通信错误"); ; }
                if (rev.result_code != "SUCCESS")
                { return BackMessage("业务出错"); }
                if (rev.sign == Utils.GetPaySign(rev, payconfig.PayKey))
                {
                    //回调函数，业务逻辑处理，处理结束后返回信息给微信
                    return BackMessage(action(rev) ? "" : "商户处理订单信息错误");
                }
                errCallBack?.Invoke(reqdata, new Exception("签名错误"));
                return BackMessage("签名错误");
            }
            catch (Exception e)
            {
                errCallBack?.Invoke(reqdata, e);
                return BackMessage("回调函数处理错误");
            }
        }

        public static string GetRefundNofity(this HttpContext context, Func<RefundNotify, bool> func, string mchId = "")
        {
            var reqdata = context.GetRequestData();
            var payconfig = OpenApi.GetPayConfig(mchId);
            var doc = XElement.Parse(reqdata);
            var rev = XmlUtil.ToObject<RefundNotifyOrigin>(doc);
            var stringB = Utils.GetDecding(rev.req_info);
            var enKey = Utils.MD5(payconfig.PayKey).ToLower();
            var result = AESUtil.Decrypt(stringB, enKey);
            var revResult = XmlUtil.ToObject<RefundNotify>(XElement.Parse(result));
            if (func.Invoke(revResult))
            {
                return BackMessage();
            }
            else
            {
                return BackMessage("处理错误");
            }
        }

        public static string BackMessage(string msg = "")
        {
            Dictionary<string, string> dic = new Dictionary<string, string>();
            if (msg != "")
            {
                dic.Add("return_code", "FAIL");
                dic.Add("return_msg", msg);
            }
            else
            {
                dic.Add("return_code", "SUCCESS");
                dic.Add("return_msg", "OK");
            }

            return Utils.parseXML(dic);
        }


#if !NET40

        /// <summary>
        /// 下载对账单。如果启用压缩，请将文件流保存为gzip格式。否则保存为csv
        /// </summary>
        /// <param name="req"></param>
        /// <param name="stream"></param>
        /// <param name="mchId"></param>
        /// <returns></returns>
        public static async Task<BasePayRes> DownloadBillAsync(DownloadBill req, FileStreamInfo stream, string mchId = "")
        {
            var url = $"https://api.mch.weixin.qq.com/pay/downloadbill";
            var res = await req.DownLoadAsync(url, stream, mchId);
            if (res.Contains("return_code"))
            {
                return PayTools.XmlToEntity<BasePayRes>(res, "");
            }

            if (!string.IsNullOrEmpty(res))
            {
                var bytes = Encoding.UTF8.GetBytes(res);
                using (var ms = new MemoryStream(bytes))
                {
                    await ms.CopyToAsync(stream);
                    await stream.FlushAsync();
                }
            }
            return new BasePayRes { return_code = "SUCCESS", err_code = "SUCCESS" };
        }
        /// <summary>
        /// 关闭订单。以下情况需要调用关单接口：商户订单支付失败需要生成新单号重新发起支付，要对原订单号调用关单，避免重复支付；系统下单后，用户支付超时，系统退出不再受理，避免用户继续，请调用关单接口。
        /// 注意：订单生成后不能马上调用关单接口，最短调用时间间隔为5分钟。
        /// </summary>
        /// <param name="req"></param>
        /// <param name="mchId"></param>
        /// <returns></returns>
        public static async Task<BasePayRes> CloseOrderAsync(CloseOrderReq req, string mchId = "")
        {
            var url = $"https://api.mch.weixin.qq.com/pay/closeorder";
            return await req.PayRequestAsync<BasePayRes>(url, mchId);
        }
        public static async Task<MicroPayRes> MicroPayAsync(MicroPayReq req, string mchId = "")
        {
            var url = "https://api.mch.weixin.qq.com/pay/micropay";
            return await req.PayRequestAsync<MicroPayRes>(url, mchId);
        }
        /// <summary>
        ///刷卡支付， 并处理验证密码的过程。推荐使用此方法
        /// </summary>
        /// <param name="req"></param>
        /// <returns></returns>
        public static async Task<MicroPayValidPwdRes> MicroPayByVaildPwdAsync(MicroPayReq req, string mchId = "")
        {
            var result = new MicroPayValidPwdRes();
            var res = await MicroPayAsync(req, mchId);
            if (res.result_code == res.return_code && res.result_code == "SUCCESS")
            {
                result.IsSuccess = true;
                result.OpenId = res.openid;
                result.TradeNo = res.transaction_id;
                return result;
            }

            if (res.err_code == "USERPAYING")
            {
                for (int i = 0; i < 10; i++)
                {
                    Thread.Sleep(3000);
                    var orderinfo = await QueryOrderInfoAsync(new QueryOrderReq
                    {
                        sub_mch_id = req.sub_mch_id,
                        out_trade_no = req.out_trade_no
                    }, mchId);
                    if (orderinfo.trade_state == TradeState.USERPAYING)
                    {
                        continue;
                    }
                    if (orderinfo.trade_state == TradeState.SUCCESS)
                    {

                        result.IsSuccess = true;
                        result.OpenId = orderinfo.openid;
                        result.TradeNo = orderinfo.transaction_id;
                        return result;
                    }
                }
                var re = await ReverseAsync(new ReverseReq
                {
                    sub_mch_id = req.sub_mch_id,
                    out_trade_no = req.out_trade_no
                }, mchId);
                result.IsSuccess = false;
                result.ErrMsg = "验证密码超时，支付失败";
                return result;
            }
            else
            {
                result.IsSuccess = false;
                result.ErrMsg = res.err_code;
                return result;
            }
        }
        /// <summary>
        /// 统一下单接口
        /// </summary>
        /// <param name="req"></param>
        /// <returns></returns>
        public static async Task<UnifiedRes> UnifiedOrderAsync(UnifiedReq req, string mchId = "")
        {
            var url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
            return await req.PayRequestAsync<UnifiedRes>(url, mchId);
        }
        public static async Task<MicroPayRes> MicroPayAsync(MicroPayReq req, Action<string> userPayingAction, string mchId = "")
        {
            var url = "https://api.mch.weixin.qq.com/pay/micropay";
            var res = await req.PayRequestAsync<MicroPayRes>(url, mchId);
            if (res.result_code == "USERPAYING")
            {
                userPayingAction(req.out_trade_no);
            }
            return res;
        }
        /// <summary>
        /// 查询订单状态
        /// </summary>
        /// <param name="req"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static async Task<QueryOrderRes> QueryOrderInfoAsync(QueryOrderReq req, string mchId = "")
        {
            var url = "https://api.mch.weixin.qq.com/pay/orderquery";
            return await req.PayRequestAsync<QueryOrderRes>(url, mchId);
        }
        public static async Task<ReverseRes> ReverseAsync(ReverseReq req, string mchId = "")
        {
            var url = "https://api.mch.weixin.qq.com/secapi/pay/reverse";
            var res = await req.PayRequestAsync<ReverseRes>(url, mchId);
            if (res.recall == "Y")
            {
                Thread.Sleep(2000);
                return await ReverseAsync(req);
            }
            return res;
        }
        /// <summary>
        /// 退款接口
        /// </summary>
        /// <param name="req"></param>
        /// <param name="mchId"></param>
        /// <returns></returns>
        public static async Task<RefundRes> RefundAsync(RefundReq req, string mchId = "")
        {
            var url = $"https://api.mch.weixin.qq.com/secapi/pay/refund";
            return await req.PayRequestAsync<RefundRes>(url, mchId);
        }
        /// <summary>
        /// 获取退款订单
        /// </summary>
        /// <param name="req"></param>
        /// <param name="mchId"></param>
        /// <returns></returns>
        public static async Task<QueryRefundOrderRes> QueryRefundOrderAsync(QueryRefundOrderReq req, string mchId = "")
        {
            var url = "https://api.mch.weixin.qq.com/pay/refundquery";
            return await req.PayRequestAsync<QueryRefundOrderRes>(url, mchId);
        }
#endif
    }
}